/*
 * DatagramBuilder Library: serialize/deserialize data to/from datagram
 * Copyright (C) 2008 Rakov Ilya <RakovIlya@gmail.com>
 *
 * This library can be distributed under the terms of the GNU LGPLv2.
 * See the file COPYING.LIB.
 */


#ifndef DATAGRAM_SERIALIZEIMPL_H_
#define DATAGRAM_SERIALIZEIMPL_H_


/**
 * @file SerializeImpl.h
 *
 * @brief SerializeImpl implementation file
 *
 * @date Dec 2, 2008
 * @author Rakov Ilya
 */

#include <boost/type_traits.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>

#include <sstream>


using boost::tuples::tuple;
using boost::tuples::cons;
using boost::tuples::null_type;


template< class T >
struct SerializeImpl;


template< class TAG_T, TAG_T TAG >
struct TagBinder
{
  static inline void SerializeTag( Builder &builder )
  {
    builder.SerializeValue( TAG );
  }

  static void DeSerializeTag( Parser &parser )
  {
    TAG_T tag;

    parser.DeSerializeValue( tag );

    if ( TAG != tag )
    {
      std::stringstream ss;

      ss << "deserialized tag is \"" << tag
         << "\", when the requested tag is \"" << TAG << "\"";

      throw deserialize_error( ss.str() );
    }

  }
};


template < class T >
struct RemoveConst
{
  typedef typename boost::remove_const< T >::type type;
};


template < class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 >
struct RemoveConst< tuple< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 > >
{
  typedef tuple< typename RemoveConst< T0 >::type,
                 typename RemoveConst< T1 >::type,
                 typename RemoveConst< T2 >::type,
                 typename RemoveConst< T3 >::type,
                 typename RemoveConst< T4 >::type,
                 typename RemoveConst< T5 >::type,
                 typename RemoveConst< T6 >::type,
                 typename RemoveConst< T7 >::type,
                 typename RemoveConst< T8 >::type,
                 typename RemoveConst< T9 >::type > type;
};


template < class T >
struct SerializeImpl< const T >
{

  static inline void SerializeTag( Builder &builder )
  {
    SerializeImpl< T >::SerializeTag( builder );
  }


  static inline void Serialize( Builder &builder, const T &value )
  {
    SerializeImpl< T >::Serialize( builder, value );
  }


  static inline void DeSerializeTag( Parser &parser )
  {
    SerializeImpl< T >::DeSerializeTag( parser );
  }


  static void DeSerialize( Parser &parser, const T &value )
  {
    typename RemoveConst< T >::type tmp;

    SerializeImpl< T >::DeSerialize( parser, tmp );

    if ( value != tmp )
    {
      std::stringstream ss;

      ss << "deserialized value is \"" << tmp
         << "\", when the requested value is \"" << value << "\"";

      throw deserialize_error( ss.str() );
    }
  }

};


template< class T >
struct TupleTag;


template< class T1 >
struct TupleTag< cons<T1, null_type> >
{

  typedef typename boost::remove_reference< T1 >::type type1;


  static inline void Serialize( Builder &builder )
  {
    SerializeImpl< type1 >::SerializeTag( builder );
  }


  static inline void DeSerialize( Parser &parser )
  {
    SerializeImpl< type1 >::DeSerializeTag( parser );
  }

};


template< >
struct TupleTag< null_type >
{

  static inline void Serialize( Builder & )
  {
  }


  static inline void DeSerialize( Parser & )
  {
  }

};


template< class T1, class T2 >
struct TupleTag< cons<T1, T2> >
{

  typedef typename boost::remove_reference< T1 >::type type1;
  typedef typename boost::remove_reference< T2 >::type type2;


  static inline void Serialize( Builder &builder )
  {
    SerializeImpl< type1 >::SerializeTag( builder );
    SerializeImpl< type2 >::SerializeTag( builder );
  }


  static inline void DeSerialize( Parser &parser )
  {
    SerializeImpl< type1 >::DeSerializeTag( parser );
    SerializeImpl< type2 >::DeSerializeTag( parser );
  }

};



template< class T >
struct SerializeImpl
{

  typedef typename boost::remove_const< typename T::head_type >::type head_type;
  typedef typename boost::remove_const< typename T::tail_type >::type tail_type;


  static inline void SerializeTag( Builder &builder )
  {
    TupleTag< cons< head_type, tail_type > >::Serialize( builder );
  }


  template< class T1 >
  static inline void Serialize( Builder &builder, const cons<T1, null_type> &value )
  {
    SerializeImpl< T1 >::Serialize( builder, value.get_head() );
  }


  static inline void Serialize( Builder &, const null_type & )
  {
  }


  template< class T1, class T2 >
  static inline void Serialize( Builder &builder, const cons<T1, T2> &value )
  {
    SerializeImpl< T1 >::Serialize( builder, value.get_head() );

    Serialize( builder, value.get_tail() );
  }


  static inline void DeSerializeTag( Parser &parser )
  {
    TupleTag< cons< head_type, tail_type > >::DeSerialize( parser );
  }


  template< class T1 >
  static inline void DeSerialize( Parser &parser, cons<T1, null_type> &value )
  {
    SerializeImpl< T1 >::DeSerialize( parser, value.get_head() );
  }


  static inline void DeSerialize( Parser &, null_type & )
  {
  }


  template< class T1, class T2 >
  static inline void DeSerialize( Parser &parser, cons<T1, T2> &value )
  {
    SerializeImpl< T1 >::DeSerialize( parser, value.get_head() );

    DeSerialize( parser, value.get_tail() );
  }

};


template < class TAG_T, TAG_T TAG >
struct StringSerializeImpl: TagBinder< TAG_T, TAG >
{

  static inline void Serialize( Builder &builder, const std::string &value )
  {
    builder.SerializeValue< uint32_t >( value.data(), value.data() + value.size() );
  }


  static inline void DeSerialize( Parser &parser, std::string &value )
  {
    value.clear();

    parser.DeSerializeValue< uint32_t >( std::back_inserter( value ) );
  }

};


template < class TAG_T, TAG_T TAG, class T >
struct FixedSerializeImpl: TagBinder< TAG_T, TAG >
{

  static inline void Serialize( Builder &builder, const T value )
  {
    builder.SerializeValue( value );
  }


  static inline void DeSerialize( Parser &parser, T &value )
  {
    parser.DeSerializeValue( value );
  }

};

#endif /* DATAGRAM_SERIALIZEIMPL_H_ */
